其他

GLSL Shader 范例集 [ 001 - 020 ]

2017-05-15 Wenzy InsLab

范例集是个人在学习 GLSL 编程时的练习梳理,部分范例参考自《 The Book Of Shaders 》,其余部分根据个人理解,加入延展扩充

什么是 Shader?

Shader 编程入门初探-CPU  Vs  GPU

阅读指南

  • 若此前未接触过 shader 编程,推荐结合《 The Book Of Shaders 》一书(  作者是 Patricio Gonzalez VivoJen Lowe。前 11 章有中文翻译,译者为 TornoteArtrustee

  • 下文的范例使用框架为 Openframeworks。改写到 Processing 上运行可参考
    (
    )

  • 范例从最基础的概念开始,以应用为导向,由浅入深。通过目录可快速回顾相关知识点

准备-Xcode 中的简便操作技巧

下面分享一个小技巧,可以在 Xcode 中快速编辑 shader

  • 准备工作,先在 data 中创建文件 shader.frag 并保存

  • 创建文件后,可在左侧创建一个名为 shader 的文件夹。方法是在项目名处右键,点 new Group。之后将 shader.frag 拖动到其中,接着勾选 copy items if needed ,并点 finish。

  • 现在就可以在 xcode 中仅仅通过点击来切换文件,方便随时修改 shader

Shader 范例集目录

  • 001-对屏幕平涂指定颜色

  • 002-确定一个分界线,对屏幕指定的一边平涂颜色

  • 003-绘制宽度为 1 像素的垂直直线

  • 004-绘制宽度为指定像素的垂直直线

  • 005-绘制矩形

  • 006-绘制圆形

  • 004-绘制垂直直线

  • 005-传入时间变量,自动变色

  • 006-传入鼠标位置,在shader中移动圆

  • 007-传入外部变量,随时间变色

  • 008-传入鼠标坐标,在shader中移动圆

  • 009-绘制过原点的直线, mouseX 控制斜率

  • 010-绘制竖条纹,mouseX 控制条纹宽度

  • 011-绘制黑白格子,mouseX 控制格子宽度

  • 012-绘制 sin 函数曲线,鼠标控制曲线位移

  • 013-混合指定两种颜色

  • 014-绘制带软阴影的圆

  • 015-绘制带软阴影的矩形

  • 016-绘制 smoothstep 函数的变化曲线

  • 017-绘制只带描边的圆形

  • 018-绘制只带描边的矩形

  • 019-变换坐标系以适配屏幕

  • 020-绘制距离场,对比 length 与 dot

001-对屏幕平涂指定颜色

知识点:理解如何在 OF 中加载 shader,理解 gl_FragColor ,vec4 的概念。gl_FragColor 可以理解为当前像素的色值,vec4 可用于表示色彩,分别对应 R,G,B,A , 最大值为 1

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    gl_FragColor = vec4(1.0,0.0,0.0,1.0); }

  • 建议 vec4() 中的数值写成小数,虽然某些显卡上不会报错,但写成小数会更规范

  • shader 的效果可以理解成一个贴图,在上面例子中,通过 begin 和 end,它就会将效果投射到与画布等大的矩形中。可以试着修改矩形的比例或是换成其他绘图函数对照效果

002-确定一个分界线,对屏幕指定的一边平涂颜色

知识点:理解 gl_FragCoord

方法一:

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    if(gl_FragCoord.x < 200.0){        gl_FragColor = vec4(1.0,0.0,0.0,1.0);    }else{        gl_FragColor = vec4(vec3(0.0),1.0);    } }

方法二:

知识点:理解 step

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    float val = step(gl_FragCoord.x,200.0);    gl_FragColor = vec4(val * vec3(1.0,0.0,0.0),1.0); }

  • shader 编程中,多用 step 函数来替换 if。此方法更简洁,规范。

  • step(float a,float b) 是一个插值函数,它需要输入两个参数。第一个是阀值,第二个是传入的判定数值。当 a <= b,则返回 1.0。若 a > b,则返回 0.0。

  • float val = step(gl_FragCoord.x,200.0) 等价于 float val = 1 -step(200.0,gl_FragCoord.x);

003-绘制宽度为 1 像素的垂直直线

方法一:绘制宽度为 1 像素的直线

知识点:更准确地理解 gl_FragCoord 的分量 x,y。其中的小数部分恒为0.5

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    if(gl_FragCoord.x == 200.5){        gl_FragColor = vec4(1.0,1.0,1.0,1.0);    }else{        gl_FragColor = vec4(0.0,0.0,0.0,1.0);    } }

  • 小数部分若不写 .5 , 直线不会被显示出来

  • 绘制横向的直线则写成 gl_FragCoord.y

方法二:绘制宽度为 1 像素的直线

知识点:

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    float val = step(200.5,gl_FragCoord.x) *  step(gl_FragCoord.x,200.5);    gl_FragColor = vec4(val * vec3(1.0),1.0); }

  • 并不是绘制直线的最优写法,但有助于透彻理解 step 函数。

  • 乘号相当于 &&

004-绘制宽度为指定像素的垂直直线

知识点:理解 step,上例的延伸版

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

void main(void){    float w = 50.0;  // 直线宽度    float startX = 300.5;   // 起始位置    vec3 color = vec3(step(startX - w/2,gl_FragCoord.x) * step(gl_FragCoord.x,startX + w/2));    gl_FragColor = vec4(color,1.0); }

005-绘制矩形

方法一:

知识点:理解 gl_FragCoord

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    if(gl_FragCoord.x > 200.0 && gl_FragCoord.x < 600.0 && gl_FragCoord.y > 200.0 && gl_FragCoord.y < 400.0){        gl_FragColor = vec4(1.0,0.0,0.0,1.0);    }else{        gl_FragColor = vec4(0.0,0.0,0.0,1.0);    } }

  • 理解绘制矩形的判定条件

方法二:

知识点:深入理解 step,可传入 vec2 类型

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    vec2 left = step(vec2(200,200),gl_FragCoord.xy);    vec2 right = step(gl_FragCoord.xy,vec2(600,400));    float val = left.x * left.y * right.x * right.y;    gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val,1.0); }

  • step 函数中传入 vec2 类型可以这样理解。

     vec2 left = step(vec2(200,200),gl_FragCoord.xy)

    等价于

     vec2 left;  left.x = step(200,gl_FragCoord.x);  left.y = step(200,gl_FragCoord.y);

006-绘制圆形

方法一:

知识点:理解 distance ,vec2 的用法

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    vec2 center = vec2(512,350);    if(distance(center,gl_FragCoord.xy) < 200){        gl_FragColor = vec4(1.0,0.0,0.0,1.0);    }else{        gl_FragColor = vec4(0.0,0.0,0.0,1.0);    } }

  • vec2 可表示二维点坐标

  • distance 函数必须传入两个 vec2 类型,gl_FragCoord 是 vec4 类型,因此不能直接使用,需要写作 gl_FragCoord.xy。它等价于 vec2(gl_FragCoord.x,gl_FragCoord.y)

  • 补充知识:gl_FragCoord 的四个分量分别对应x, y, z和1/w。其中,x和y是当前片元的窗口相对坐标,但不是整数,小数部分恒为0.5。详细说明可参考(

方法二:

知识点:理解 step

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 void main(void){    vec2 center = vec2(512,350);    vec3 color = vec3(step(200,distance(center,gl_FragCoord.xy)));    gl_FragColor = vec4(color,1.0); }

007-传入外部变量,随时间变色

知识点:理解 setUniform1f ,从主程序传入数据

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){     ofBackground(0);    shader.begin();    shader.setUniform1f("u_time", ofGetElapsedTimef());    ofDrawCircle(mouseX,mouseY,200);    shader.end(); }

— Fragment Shader 内

#version 120 uniform float u_time;  // 记得加上分号 void main( void ){    float r = abs(sin(u_time));    float g = abs(cos(u_time));    float b = 0.5;    gl_FragColor = vec4(r,g,b,1.0); }

  • 注意,函数 setUniform1f 必须放在 begin 和 end 之内

008-传入鼠标坐标,在shader中移动圆

知识点:理解 setUniform2f

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    float val = step(distance(u_mouse,gl_FragCoord.xy),100);    gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val,1.0); }

  • 注意,因为 GPU 中的坐标与 OF 中的纵坐标朝向是相反的,所以需要在传入时,需写成 ofGetHeight() - mouseY

009-绘制过原点的直线, mouseX 控制斜率

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    float k = u_mouse.x/100.0;    float y = gl_FragCoord.x * k; // 直线方程    float val = step(gl_FragCoord.y,y) * step(y,gl_FragCoord.y + 20.0);    gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val,1.0); }

010-绘制竖条纹,mouseX 控制条纹宽度

知识点:理解 mod

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    float interval = u_mouse.x / 10.0;    if(mod(int(gl_FragCoord.x / interval),2) == 0){        gl_FragColor = vec4(1.0,1.0,1.0,1.0);    }else{        gl_FragColor = vec4(0.0,0.0,0.0,1.0);    } }

  • mod(x,y) 为取模运算,等价于  x % y

011-绘制黑白格子,mouseX 控制格子宽度

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    float interval = u_mouse.x / 10.0;    if(mod(int(gl_FragCoord.x / interval),2) == 0){        if(mod(int(gl_FragCoord.y / interval),2) == 0){            gl_FragColor = vec4(1.0,1.0,1.0,1.0);        }else{            gl_FragColor = vec4(0.0,0.0,0.0,1.0);        }    }else{        if(mod(int(gl_FragCoord.y / interval),2) == 0){            gl_FragColor = vec4(0.0,0.0,0.0,1.0);        }else{            gl_FragColor = vec4(1.0,1.0,1.0,1.0);        }    } }

012-绘制 sin 函数曲线,鼠标控制曲线位移

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    float y = 300 + sin((gl_FragCoord.x + u_mouse.x)/100.0) * 200;    float val = step(gl_FragCoord.y,y + u_mouse.y) * step(y + u_mouse.y,gl_FragCoord.y + 20);    gl_FragColor = vec4(vec3(val),1.0); }

013-混合指定两种颜色

知识点:理解 mix 函数,作用是混合色彩

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    shader.setUniform2f("u_resolution",ofGetWidth(),ofGetHeight());    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; uniform vec2 u_resolution; void main(void){    vec4 colorA,colorB;    colorA = vec4(1.0,0.0,0.0,1.0);    colorB = vec4(0.0,1.0,0.0,1.0);    gl_FragColor = mix(colorA,colorB,u_mouse.x/u_resolution.x); }

014-绘制带软阴影的圆

知识点:理解 length ,smoothstep

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    shader.setUniform2f("u_resolution",ofGetWidth(),ofGetHeight());    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; uniform vec2 u_resolution; void main(void){    vec2 center = u_resolution.xy/2;    float shadowW = u_mouse.x/5.0;    float dist = length(gl_FragCoord.xy - center);    if(dist < 100){        gl_FragColor = vec4(0,0,0,1);    }else if(dist < 100 + shadowW){        vec4 colorA = vec4(vec3(0.7),1.0);        vec4 colorB = vec4(vec3(1.0),1.0);        // 使用 smoothstep        gl_FragColor = mix(colorA,colorB,smoothstep(100,100 + shadowW,dist));        // 不使用 smoothstep        //gl_FragColor = mix(colorA,colorB,(dist - 100.0)/shadowW);    }else{        gl_FragColor = vec4(1.0,1.0,1.0,1.0);    } }

  • length(a - b) 等价于 distance(a,b),它能计算某向量的长度

  • smoothstep(float a,float b,float x)函数,它的返回值固定为 0 到 1 之间的过渡值。a,b 表示 x 范围的上下限,x 代表传入的参数。输出值的变化是非线性的,会作平滑处理

  • smoothstep 适当使用,可起抗锯齿效果

015-绘制带软阴影的矩形

知识点:理解 length ,smoothstep

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    vec2 ptA = vec2(300,300);    vec2 ptB = vec2(700,400);    if(step(ptA,gl_FragCoord.xy) * step(gl_FragCoord.xy,ptB) == vec2(1.0)){        gl_FragColor = vec4(vec3(0.0),1.0);    }else{        float shadowW = u_mouse.x/10.0;        vec2 left = smoothstep(ptA - vec2(shadowW),ptA ,gl_FragCoord.xy);        vec2 right = smoothstep(ptB + vec2(shadowW),ptB,gl_FragCoord.xy);        vec3 color = 1 - vec3(left.x * left.y * right.x * right.y) * 0.5;        gl_FragColor = vec4(color,1.0);    } }

016-绘制 smoothstep 函数的变化曲线

知识点:深入理解 smoothstep,理解 abs

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    shader.setUniform2f("u_resolution",ofGetWidth(),ofGetHeight());    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; uniform vec2 u_resolution; void main(void){    float h = 300;    if(gl_FragCoord.y < h){        float ratio = gl_FragCoord.x / u_resolution.x;        float val = step(abs(gl_FragCoord.y/h - ratio),0.01);        gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val + vec3(ratio) * (1 - val),1.0);    }else if(gl_FragCoord.y > h + 100){        float ratio = smoothstep(0,u_resolution.x,gl_FragCoord.x);        float val = step(abs((gl_FragCoord.y - 400.0)/h - ratio),0.01);        gl_FragColor = vec4(vec3(1.0,0.0,0.0) * val + vec3(ratio) * (1 - val),1.0);    }else{        gl_FragColor = vec4(0.0,0.0,0.0,1.0);    } }

017-绘制只带描边的圆形

知识点:理解 step

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 float drawCircle(vec2 pos,float r,float lineWidth){    float r1 = r - lineWidth/2;    float r2 = r + lineWidth/2;    return step(r1,distance(gl_FragCoord.xy,pos)) * step(distance(gl_FragCoord.xy,pos),r2); } void main(void){    vec2 center = vec2(512,350);    gl_FragColor = vec4(vec3(drawCircle(center,200,20)),1.); }

  • 自定义函数必须写在 main 前

018-绘制只带描边的矩形

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 float drawRect(vec2 pos,vec2 size,float lineWidth){    // 外部矩形    vec2 rect1_pos1 = pos - size/2 - vec2(lineWidth/2);    vec2 rect1_pos2 = pos + size/2 + vec2(lineWidth/2);    vec2 rect1_bl = step(rect1_pos1,gl_FragCoord.xy);    vec2 rect1_tr = step(gl_FragCoord.xy,rect1_pos2);    // 内部矩形    vec2 rect2_pos1 = pos - size/2 + vec2(lineWidth/2);    vec2 rect2_pos2 = pos + size/2 - vec2(lineWidth/2);    vec2 rect2_bl = step(rect2_pos1,gl_FragCoord.xy);    vec2 rect2_tr = step(gl_FragCoord.xy,rect2_pos2);    return rect1_bl.x * rect1_bl.y * rect1_tr.x * rect1_tr.y * (1.0 - rect2_bl.x * rect2_bl.y * rect2_tr.x * rect2_tr.y); } void main(void){    vec2 center = vec2(512,350);    gl_FragColor = vec4(vec3(drawRect(center,vec2(600,400),20)),1.); }

019-变换坐标系以适配屏幕

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_resolution", ofGetWidth(),ofGetHeight());    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_resolution; float drawCircle(vec2 curPos,vec2 pos,float r,float lineWidth){    float r1 = r - lineWidth/2;    float r2 = r + lineWidth/2;    return step(r1,distance(curPos,pos)) * step(distance(curPos,pos),r2); } void main(void){    vec2 st = gl_FragCoord.xy/u_resolution;    st -= vec2(0.5);    st *= 2.0;    // 对比开启与关闭的区别    st.x *= u_resolution.x/u_resolution.y;    vec2 center = vec2(0.0);    gl_FragColor = vec4(vec3(drawCircle(st,center,0.5,0.1)),1.); }

  • 思路:将坐标的变化范围进行映射,例如以 y 轴作为基准。原本 y 的坐标变化是从 0 到屏幕宽,现在映射为 -1 到 1。为了让图形不产生拉伸,x 轴的变化范围则需要根据屏幕的宽高比进行调整。经过系列操作,坐标原点(0,0)会处于屏幕中央

  • 优点1:此方法可以确保在不同分辨率的屏幕上,比例可以自适应

  • 优点2:绘制对称图形,坐标表达会更方便

020-绘制距离场,对比 length 与 dot

方法一:使用 length

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    float l = length(gl_FragCoord.xy - vec2(512,350));    gl_FragColor = vec4(vec3(fract(u_mouse.x/10000.0 * l)),1.); }

  • 函数 fract(float x) 的作用是去掉整数部分,只取小数部分。因此产生黑白相间的循环效果

方法二:使用 dot

— ofApp.h 内

ofShader shader;

— ofApp.cpp 内

void ofApp::update(){    shader.load("shader"); } void ofApp::draw(){    shader.begin();    shader.setUniform2f("u_mouse", mouseX,ofGetHeight() - mouseY);    ofDrawRectangle(0,0, ofGetWidth(), ofGetHeight());    shader.end(); }

— Fragment Shader 内

#version 120 uniform vec2 u_mouse; void main(void){    vec2 temp = gl_FragCoord.xy - vec2(512,350);    gl_FragColor = vec4(vec3(fract(u_mouse.x/1000000.0 * dot(temp,temp))),1.); }

  • 使用 dot 函数,图形的计算效率会更高。但使用它绘制得到的距离场是非线性变化的

END

学习 Shader 的过程中有一个体会。在 Processing 或 Openframeworks 中使用已有的绘图函数,就像是用现成的基本原料来搭建东西。这些原料是木材,是石头。需要你一砖一瓦地构建才能做出高楼。而用 shader 编程,就像是拥有一台性能极强的 3D 打印机,可以允许在分子原子层级去搭建物体。这种神奇的力量是由 GPU 的强大性能所赋予的。我们要做的只是提前编写好逻辑,考虑好局部像素与全局图形之间的关系,它便能帮你瞬间造物(图像)。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存